6장 컴포넌트 반복
데이터 배열을 컴포넌트 배열로 변환
map 함수 이용해서 배열들을 컴포넌트로 구성된 배열을 생성할 수 있다.
const IterationSample = () => {
const names = ["눈사람", "얼음", "눈", "바람"];
const names2 = [
<li>눈사람</li>,
<li>얼음</li>,
<li>눈</li>,
<li>바람바람</li>,
];
const name3 = [
"<li>눈사람</li>",
"<li>얼음</li>",
"<li>눈</li>",
"<li>바람바람</li>",
];
const nameList = names.map((name) => <li>{name}</li>);
return (
<ul>
{nameList}(
{names2}
</ul>
);
};
name3 처럼 문자열로 넣어줘도 될 줄 알았는데, 안됐다. react로 작성된 코드들은 어디서든 JSX가 적용되는 모양이다.
브라우저에 잘 나오지만, key prop이 없다는 에러가 발생하낟.
key
리액트에서 key는 컴포넌트 배열을 렌덩링 할때 어떤 원소에 변동이 있었는지 알아내기 위해 사용함. key 없으면 virtual DOM이랑 비교할 때, 리스트를 순차적으로 비교하지만, key가 있으면 key 값 사용해서 빠르게 변화 감지 할 수 있음.
함수 내부에서 컴포넌트 props를 설정하듯이 설정하면 됨.
key 값은 언제나 유일해야한다.
const IterationSample = () => {
const names = ['눈사람', '얼음', '눈', '바람'];
const namesList = names.map((name, index) => <li key={index}>{name}</li>);
return <ul>{namesList}</ul>;
};
사실 이렇게 고유한 값이 없을 때, index 값을 key로 사용하면 비효율적임.
→ 고유값이 없으면 고유값을 만들어 사용해라.
데이터를 추가 구현
const IterationSample = () => {
const [names, setNames] = useState([
{ id: 1, text: "눈사람" },
{ id: 2, text: "얼음" },
{ id: 3, text: "눈" },
{ id: 4, text: "바람" },
]);
const [inputText, setInputText] = useState("");
const [nextId, setNextId] = useState(5);
const nameList = names.map((name) => <li key={name.id}>{name.text}</li>);
const handleChange = (e) => {
setInputText(e.target.value);
};
const handleClick = () => {
setNames([...names, { id: nextId, text: inputText }]);
setNextId(nextId + 1);
setInputText("");
};
return (
<div>
<ul>{nameList}</ul>
<input
value={inputText}
onChange={handleChange}
placeholder="추가할 내용 입력"
/>
<button onClick={handleClick}>추가</button>
</div>
);
};
- 함수형에서 state 에는 객체가 아닌 값도 들어갈 수 있음. 그러나 객체 사용할 때는 바꾸려고하는 프로퍼티만 satet함수로 넘겨줬는데, 배열이여서 스프레드 함수로 기존 값을 가져와서 바꾸는 모습.
- 스프레드 말고 array.concat을 사용해도 된다. push를 사용하지 않는 이유는 기존 배열을 변경하기 때문.
리액트에서 상태를 업데이트할 때는 기존 상태를 그대로 두면서 새로운 값을 상태로 설정해야한다. 이러한 불변성을 유지해줘야 성능을 춰적화할 수 있다고 한다.
데이터 제거 구현
데이터 불변성을 유지하면서 업데이트 해주기 위해서 배열의 내장함수 filter을 이용해야한다.
더블클릭을 하면 해당 요소가 삭제 된다.
const handleDoubleClick = (e) => {
const newNames = names.filter((name) => name.id != e.target.key);
setNames(newNames);
};
const nameList = names.map((name) => (
<li key={name.id} onDoubleClick={handleDoubleClick}>
{name.text}
</li>
));
- 처음에 이렇게 구현했는데, 작동하지 않아서 보니 e.target.key가 undefined값을 반환한다.
→ key 값은 DOM 요소에 속성으로 묶이는게 아닌 것 같다. 리액트에서만 사용하는 건가?
const handleDoubleClick = (id) => {
const newNames = names.filter((name) => name.id != id);
setNames(newNames);
};
const nameList = names.map((name) => (
<li key={name.id} onDoubleClick={() => handleDoubleClick(name.id)}>
{name.text}
</li>
));
위와 같은 이유로 name.id를 인자로 넘겨주는 식으로 호출하는걸로 바꿨더니 잘 된다.